package com.icongyou.enterprise.talent_analysis.controller;

import com.icongyou.enterprise.talent_analysis.entity.PortraitResponse;
import com.icongyou.enterprise.talent_analysis.entity.DimensionDetail;
import com.icongyou.enterprise.talent_analysis.entity.dto.StudentFeatureContext;
import com.icongyou.enterprise.talent_analysis.service.*;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;

import java.util.*;

import static org.mockito.ArgumentMatchers.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import static org.hamcrest.Matchers.*;

/**
 * 能力画像控制器测试
 */
@SpringBootTest
@AutoConfigureMockMvc
public class PortraitControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private PortraitService portraitService;

    @MockBean
    private AIPortraitEvaluationService aiPortraitService;

    @MockBean
    private PortraitCacheService cacheService;

    @MockBean
    private HrTemplatePreferenceService preferenceService;

    @MockBean
    private WordCloudService wordCloudService;

    @Test
    public void testGetDefaultPortrait_fromCache() throws Exception {
        String studentId = "660e8400-e29b-41d4-a716-446655440101";
        
        PortraitResponse cachedResponse = createMockPortraitResponse();
        
        Mockito.when(cacheService.getFromCache(studentId, null))
            .thenReturn(cachedResponse);

        mockMvc.perform(get("/api/portrait/student/{studentId}", studentId))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.code").value(200))
            .andExpect(jsonPath("$.data.labels", hasSize(6)))
            .andExpect(jsonPath("$.data.scores", hasSize(6)));

        // 验证从缓存读取，不调用计算服务
        Mockito.verify(portraitService, Mockito.never()).computeDefaultPortrait(anyString());
    }

    @Test
    public void testGetDefaultPortrait_cacheMiss_computeDefault() throws Exception {
        String studentId = "660e8400-e29b-41d4-a716-446655440101";
        
        PortraitResponse computedResponse = createMockPortraitResponse();
        
        Mockito.when(cacheService.getFromCache(studentId, null))
            .thenReturn(null);
        Mockito.when(portraitService.computeDefaultPortrait(studentId))
            .thenReturn(computedResponse);

        mockMvc.perform(get("/api/portrait/student/{studentId}", studentId))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.code").value(200))
            .andExpect(jsonPath("$.data.labels", hasSize(6)));

        // 验证计算并更新缓存
        Mockito.verify(portraitService).computeDefaultPortrait(studentId);
        Mockito.verify(cacheService).asyncUpdateCache(eq(studentId), isNull(), eq(computedResponse));
    }

    @Test
    public void testGetDefaultPortrait_withHrUserAndTemplate() throws Exception {
        String studentId = "660e8400-e29b-41d4-a716-446655440101";
        String hrUserId = "hr001";
        String templateId = "template123";
        
        PortraitResponse customResponse = createMockPortraitResponse();
        
        Mockito.when(preferenceService.getCurrentTemplateId(hrUserId))
            .thenReturn(templateId);
        Mockito.when(cacheService.getFromCache(studentId, templateId))
            .thenReturn(null);
        Mockito.when(aiPortraitService.computeCustomPortrait(studentId, templateId))
            .thenReturn(customResponse);

        mockMvc.perform(get("/api/portrait/student/{studentId}", studentId)
                .param("hrUserId", hrUserId))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.code").value(200));

        // 验证使用自定义模板
        Mockito.verify(preferenceService).getCurrentTemplateId(hrUserId);
        Mockito.verify(aiPortraitService).computeCustomPortrait(studentId, templateId);
        Mockito.verify(cacheService).asyncUpdateCache(studentId, templateId, customResponse);
    }

    @Test
    public void testGetDefaultPortrait_withHrUserNoTemplate() throws Exception {
        String studentId = "660e8400-e29b-41d4-a716-446655440101";
        String hrUserId = "hr001";
        
        PortraitResponse defaultResponse = createMockPortraitResponse();
        
        Mockito.when(preferenceService.getCurrentTemplateId(hrUserId))
            .thenReturn(null); // HR用户未设置模板
        Mockito.when(cacheService.getFromCache(studentId, null))
            .thenReturn(null);
        Mockito.when(portraitService.computeDefaultPortrait(studentId))
            .thenReturn(defaultResponse);

        mockMvc.perform(get("/api/portrait/student/{studentId}", studentId)
                .param("hrUserId", hrUserId))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.code").value(200));

        // 验证使用默认模板
        Mockito.verify(portraitService).computeDefaultPortrait(studentId);
    }

    @Test
    public void testGetCustomPortrait_success() throws Exception {
        String studentId = "660e8400-e29b-41d4-a716-446655440101";
        String templateId = "custom-template-001";
        
        PortraitResponse customResponse = createMockPortraitResponse();
        
        Mockito.when(cacheService.getFromCache(studentId, templateId))
            .thenReturn(null);
        Mockito.when(aiPortraitService.computeCustomPortrait(studentId, templateId))
            .thenReturn(customResponse);

        String requestBody = "{\"templateId\":\"" + templateId + "\"}";

        mockMvc.perform(post("/api/portrait/student/{studentId}/custom", studentId)
                .contentType(MediaType.APPLICATION_JSON)
                .content(requestBody))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.code").value(200))
            .andExpect(jsonPath("$.data.labels", hasSize(6)));

        Mockito.verify(aiPortraitService).computeCustomPortrait(studentId, templateId);
        Mockito.verify(cacheService).asyncUpdateCache(studentId, templateId, customResponse);
    }

    @Test
    public void testGetCustomPortrait_fromCache() throws Exception {
        String studentId = "660e8400-e29b-41d4-a716-446655440101";
        String templateId = "custom-template-001";
        
        PortraitResponse cachedResponse = createMockPortraitResponse();
        
        Mockito.when(cacheService.getFromCache(studentId, templateId))
            .thenReturn(cachedResponse);

        String requestBody = "{\"templateId\":\"" + templateId + "\"}";

        mockMvc.perform(post("/api/portrait/student/{studentId}/custom", studentId)
                .contentType(MediaType.APPLICATION_JSON)
                .content(requestBody))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.code").value(200));

        // 验证从缓存读取
        Mockito.verify(aiPortraitService, Mockito.never()).computeCustomPortrait(anyString(), anyString());
    }

    @Test
    public void testGetTeacherCommentKeywords_success() throws Exception {
        String studentId = "660e8400-e29b-41d4-a716-446655440101";
        
        Map<String, Integer> keywords = new LinkedHashMap<>();
        keywords.put("优秀", 15);
        keywords.put("认真", 12);
        keywords.put("积极", 10);
        keywords.put("团队合作", 8);
        keywords.put("创新", 6);
        
        Mockito.when(wordCloudService.getWordCloud(studentId))
            .thenReturn(keywords);

        mockMvc.perform(get("/api/portrait/student/{studentId}/teacher-keywords", studentId))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.code").value(200))
            .andExpect(jsonPath("$.data.优秀").value(15))
            .andExpect(jsonPath("$.data.认真").value(12));
    }

    @Test
    public void testGetTeacherCommentKeywords_emptyResult() throws Exception {
        String studentId = "660e8400-e29b-41d4-a716-446655440101";
        
        Mockito.when(wordCloudService.getWordCloud(studentId))
            .thenReturn(new HashMap<>());

        mockMvc.perform(get("/api/portrait/student/{studentId}/teacher-keywords", studentId))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.code").value(200))
            .andExpect(jsonPath("$.data").isEmpty());
    }

    @Test
    public void testGetStudentFeatures_success() throws Exception {
        String studentId = "660e8400-e29b-41d4-a716-446655440101";
        
        StudentFeatureContext features = new StudentFeatureContext();
        features.setStudentId(studentId);
        features.setStudentName("张三");
        features.setMajor("软件工程");
        features.setClassName("2021级1班");
        
        // 设置数值指标
        features.getNumericMetrics().put("courseCount", 5);
        features.getNumericMetrics().put("avgCourseScore", 85.5);
        features.getNumericMetrics().put("teamTaskCount", 8);
        features.getNumericMetrics().put("personalTaskCount", 12);
        
        // 设置统计摘要
        StudentFeatureContext.StatisticalSummary summary = new StudentFeatureContext.StatisticalSummary();
        summary.setTotalCourses(5);
        summary.setTotalAssignments(20);
        summary.setCompletedAssignments(18);
        features.setSummary(summary);
        
        Mockito.when(aiPortraitService.extractStudentFeatures(studentId))
            .thenReturn(features);

        mockMvc.perform(get("/api/portrait/student/{studentId}/features", studentId))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.code").value(200))
            .andExpect(jsonPath("$.data.studentId").value(studentId))
            .andExpect(jsonPath("$.data.studentName").value("张三"))
            .andExpect(jsonPath("$.data.major").value("软件工程"))
            .andExpect(jsonPath("$.data.numericMetrics.courseCount").value(5))
            .andExpect(jsonPath("$.data.numericMetrics.avgCourseScore").value(85.5));
    }

    @Test
    public void testRecommendMapping_success() throws Exception {
        List<String> recommendations = Arrays.asList(
            "avg_course_score",
            "avg_assignment_score",
            "course_completion_rate"
        );
        
        Mockito.when(aiPortraitService.recommendDataMapping(anyString(), anyString()))
            .thenReturn(recommendations);

        String requestBody = "{\"dimensionName\":\"技术能力\",\"dimensionDescription\":\"评估技术掌握程度\"}";

        mockMvc.perform(post("/api/portrait/dimension/recommend-mapping")
                .contentType(MediaType.APPLICATION_JSON)
                .content(requestBody))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.code").value(200))
            .andExpect(jsonPath("$.data", hasSize(3)))
            .andExpect(jsonPath("$.data[0]").value("avg_course_score"));
    }

    @Test
    public void testGetCurrentTemplate_success() throws Exception {
        String hrUserId = "hr001";
        String templateId = "template123";
        
        Mockito.when(preferenceService.getCurrentTemplateId(hrUserId))
            .thenReturn(templateId);

        mockMvc.perform(get("/api/portrait/preference/{hrUserId}", hrUserId))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.code").value(200))
            .andExpect(jsonPath("$.data").value(templateId));
    }

    @Test
    public void testGetCurrentTemplate_noTemplate() throws Exception {
        String hrUserId = "hr001";
        
        Mockito.when(preferenceService.getCurrentTemplateId(hrUserId))
            .thenReturn(null);

        mockMvc.perform(get("/api/portrait/preference/{hrUserId}", hrUserId))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.code").value(200))
            .andExpect(jsonPath("$.data").isEmpty());
    }

    @Test
    public void testSwitchTemplate_success() throws Exception {
        String requestBody = "{\"hrUserId\":\"hr001\",\"enterpriseId\":\"ent001\",\"templateId\":\"template123\"}";

        Mockito.doNothing().when(preferenceService)
            .setCurrentTemplate("hr001", "ent001", "template123");

        mockMvc.perform(post("/api/portrait/preference/switch")
                .contentType(MediaType.APPLICATION_JSON)
                .content(requestBody))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.code").value(200));

        Mockito.verify(preferenceService).setCurrentTemplate("hr001", "ent001", "template123");
    }

    @Test
    public void testSwitchTemplate_toDefault() throws Exception {
        String requestBody = "{\"hrUserId\":\"hr001\",\"enterpriseId\":\"ent001\",\"templateId\":null}";

        Mockito.doNothing().when(preferenceService)
            .setCurrentTemplate("hr001", "ent001", null);

        mockMvc.perform(post("/api/portrait/preference/switch")
                .contentType(MediaType.APPLICATION_JSON)
                .content(requestBody))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.code").value(200));

        Mockito.verify(preferenceService).setCurrentTemplate("hr001", "ent001", null);
    }

    @Test
    public void testSwitchTemplate_serviceException() throws Exception {
        String requestBody = "{\"hrUserId\":\"hr001\",\"enterpriseId\":\"ent001\",\"templateId\":\"template123\"}";

        Mockito.doThrow(new RuntimeException("数据库连接失败"))
            .when(preferenceService)
            .setCurrentTemplate(anyString(), anyString(), anyString());

        mockMvc.perform(post("/api/portrait/preference/switch")
                .contentType(MediaType.APPLICATION_JSON)
                .content(requestBody))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.code").value(500))
            .andExpect(jsonPath("$.message", containsString("切换模板失败")));
    }

    // ==================== Helper Methods ====================

    private PortraitResponse createMockPortraitResponse() {
        PortraitResponse response = new PortraitResponse();
        
        List<String> labels = Arrays.asList(
            "技术能力", "问题解决", "学习能力", "协作能力", "沟通能力", "执行能力"
        );
        response.getLabels().addAll(labels);
        
        List<Double> scores = Arrays.asList(4.5, 4.0, 4.2, 3.8, 3.5, 4.1);
        response.getScores().addAll(scores);
        
        List<DimensionDetail> dimensions = new ArrayList<>();
        for (int i = 0; i < labels.size(); i++) {
            DimensionDetail dim = new DimensionDetail();
            dim.setName(labels.get(i));
            dim.setScore(scores.get(i));
            dim.setInsufficientData(false);
            dimensions.add(dim);
        }
        response.setDimensions(dimensions);
        
        return response;
    }
}
